/*
 * core.cc
 */

#include <cstdlib> // for atoi
#include <fstream>
#include "gui.h"
#include "core.h"
#include "../protocol.h"

using namespace std;
using namespace protocol;
using namespace client_server;
using client_server::Connection;
using client_server::ConnectionClosedException;

namespace newsclient {

Core::Core(const char* host, const int port) {

	conn = new client_server::Connection(host, port);

	if (! conn->isConnected()) {
		cerr << "Connection attempt failed" << endl;
		exit(1);
	}

	state = 0;
}

Core::~Core() {
	//delete conn;
}

void Core::setGui(GUI* g) {
	gui = g;
}

int Core::getState() const {
	return state;
}

void Core::run() {
	gui->input();
}

void Core::printCommands() const {
	gui->printCommands();
}

void Core::addNewsgroup(string name) {
	try {
		conn->write(Protocol::COM_CREATE_NG);
		conn->write(name);
		conn->write(Protocol::COM_END);
	}
	catch(ConnectionClosedException) {
		gui->println("Cannot connect to server.");
	}

	handleServerResponse(Protocol::ANS_CREATE_NG, &Core::printOKMessage);
}

void Core::deleteNewsgroup(string id) {
	try {
		conn->write(Protocol::COM_DELETE_NG);
		conn->writeInt(atoi(id.c_str()));
		conn->write(Protocol::COM_END);
	}
	catch(ConnectionClosedException) {
		gui->println("Cannot connect to server.");
	}
	handleServerResponse(Protocol::ANS_DELETE_NG, &Core::printOKMessage);
}

void Core::listNewsgroups() {
	try {
		conn->write(Protocol::COM_LIST_NG);
		conn->write(Protocol::COM_END);
	}
	catch(ConnectionClosedException) {
		gui->println("Cannot connect to server.");
	}

	handleServerResponse(Protocol::ANS_LIST_NG, &Core::printNameList);
}

void Core::enterNewsgroup(string id) {
	try {
		conn->write(Protocol::COM_LIST_NG);
		conn->write(Protocol::COM_END);
	}
	catch(ConnectionClosedException) {
		gui->println("Cannot connect to server.");
	}

	group_id = atoi(id.c_str());
	//state = 1; this is done in matchWithGroupID
	handleServerResponse(Protocol::ANS_LIST_NG, &Core::matchWithGroupID);
}

void Core::leaveNewsgroup() {
	state = 0;
	group_name = "";
}

void Core::addArticle(string author, string title) {
	string str, tmp;
	system("$EDITOR tmp");
	ifstream in("tmp", ios_base::in);
	while(getline(in, tmp))
		str += tmp + '\n';
	in.close();
	system("rm tmp");

	try {
		conn->write(Protocol::COM_CREATE_ART);
		conn->writeInt(group_id);
		conn->write(title);
		conn->write(author);
		conn->write(str);
		conn->write(Protocol::COM_END);
	}
	catch(ConnectionClosedException) {
		gui->println("Cannot connect to server.");
	}

	handleServerResponse(Protocol::ANS_CREATE_ART, &Core::printOKMessage);
}

void Core::deleteArticle(string id) {
	try {
		conn->write(Protocol::COM_DELETE_ART);
		conn->writeInt(group_id);   // group ID
		conn->writeInt(atoi(id.c_str()));   // article ID
		conn->write(Protocol::COM_END);
	}
	catch(ConnectionClosedException) {
		gui->println("Cannot connect to server.");
	}

	handleServerResponse(Protocol::ANS_DELETE_ART, &Core::printOKMessage);
}

void Core::listArticles() {
	try {
		conn->write(Protocol::COM_LIST_ART);
		conn->writeInt(group_id);
		conn->write(Protocol::COM_END);
	}
	catch(ConnectionClosedException) {
		gui->println("Cannot connect to server.");
	}

	handleServerResponse(Protocol::ANS_LIST_ART, &Core::printNameList);
}

void Core::getArticle(string id) {
	try {
		conn->write(Protocol::COM_GET_ART);
		conn->writeInt(group_id);   // group ID
		conn->writeInt(atoi(id.c_str()));   // article ID
		conn->write(Protocol::COM_END);
	}
	catch(ConnectionClosedException) {
		gui->println("Cannot connect to server.");
	}

	handleServerResponse(Protocol::ANS_GET_ART, &Core::printArticle);
}

void Core::quit() {
	gui->quit();
}


void Core::errorOutput(unsigned char err) {

	gui->print("ERROR: ");

	switch(err) {
	case Protocol::ERR_NG_ALREADY_EXISTS:
		gui->println("Newsgroup already exists.");
		break;
	case Protocol::ERR_ART_DOES_NOT_EXIST:
		gui->println("Article does not exist.");
		break;
	case Protocol::ERR_NG_DOES_NOT_EXIST:
		gui->println("Newsgroup does not exist.");
		break;
	default:
		gui->println("Unknown error.");
	}
}

void Core::handleServerResponse(unsigned char expected_ans, void (Core::*f)()) {

	if(conn->read() == expected_ans) {
		// endast för list newsgroup
		if(expected_ans == Protocol::ANS_LIST_NG) {
			(*this.*f)();
			if(conn->read() != Protocol::ANS_END)
				gui->println("Server response too long");
			return;
		}

		// annars kolla svaret från servern
		switch(conn->read()) {
		case Protocol::ANS_ACK:
			(*this.*f)();
			break;
		case Protocol::ANS_NAK:
			errorOutput(conn->read());
			break;
		default:
			gui->println("Server not following protocol.");
		}
		if(conn->read() != Protocol::ANS_END)
			gui->println("Server response too long");
	}
	else {
		gui->println("Server not following protocol.");
	}
}

void Core::printNameList() {
	int nr_of_items = conn->readInt();
	for(int i = 0; i < nr_of_items; i++) {
		gui->print(conn->readInt());
		gui->print("\t");
		gui->println(conn->readString());
	}
}

void Core::printArticle() {
	gui->println("\n   \033[34m" + conn->readString() + "\033[0m");
	gui->println("by \033[35m" + conn->readString() + "\033[0m \n");
	gui->println(conn->readString() + '\n');
}

void Core::matchWithGroupID() {
	int nr_of_items = conn->readInt();
	for(int i = 0; i < nr_of_items; i++) {
		if(conn->readInt() == group_id) {
			state = 1;
			group_name = conn->readString();
		}
		else {
			conn->readString();
		}
	}
	if(state == 0) {
		errorOutput(Protocol::ERR_NG_DOES_NOT_EXIST);
		group_name = "";
		group_id = 0;
	}
	return;
}

void Core::printOKMessage() {
	gui->println("Server: query complete.");
}

}
